This function waits for the specified process to terminate,
raises the appropriate exceptions in case of abnormal
termination and returns the exit status and standard error
output of the process as a tuple: (exit_code, stderr_string).
`child_rfd' must be the file descriptor for the
reading end of the pipe created by self._call_program()
whose writing end was connected by self._call_program() to
the child process's standard error.
This function reads the process's output on standard error
from `child_rfd' and closes this file descriptor once
this is done.
Notable exceptions:
DialogTerminatedBySignal
DialogError
PythonDialogErrorBeforeExecInChildProcess
PythonDialogIOError
PythonDialogBug
ProbablyPythonBug
"""
exit_info = os.waitpid(child_pid, 0)[1]
if os.WIFEXITED(exit_info):
exit_code = os.WEXITSTATUS(exit_info)
# As we wait()ed for the child process to terminate, there is no
# need to call os.WIFSTOPPED()
elif os.WIFSIGNALED(exit_info):
raise DialogTerminatedBySignal("the dialog-like program was terminated by signal %u" % os.WTERMSIG(exit_info))
else:
raise PythonDialogBug("please report this bug to the pythondialog maintainers")
if exit_code == self.DIALOG_ERROR:
raise DialogError("the dialog-like program exited with code %d (was passed to it as the DIALOG_ERROR environment variable)" % exit_code)
elif exit_code == 127:
raise PythonDialogErrorBeforeExecInChildProcess(
"perhaps the dialog-like program could not be executed; perhaps the system is out of memory; perhaps the maximum number of open file descriptors has been reached")
elif exit_code == 126:
raise ProbablyPythonBug(
"a child process returned with exit status 126; this might be the exit status of the dialog-like program, for some unknown reason (-> probably a bug in the dialog-like program); otherwise, we have probably found a python bug")
# We might want to check here whether exit_code is really one of
# DIALOG_OK, DIALOG_CANCEL, etc. However, I prefer not doing it
# because it would break pythondialog for no strong reason when new
# exit codes are added to the dialog-like program.
#
# As it is now, if such a thing happens, the program using
# pythondialog may receive an exit_code it doesn't know about. OK, the
# programmer just has to tell the pythondialog maintainer about it and
# can temporarily set the appropriate DIALOG_* environment variable if
# he wants and assign the corresponding value to the Dialog instance's
# DIALOG_FOO attribute from his program. He doesn't even need to use a
# patched pythondialog before he upgrades to a version that knows
# about the new exit codes.
#
# The bad thing that might happen is a new DIALOG_FOO exit code being
# the same by default as one of those we chose for the other exit
# codes already known by pythondialog. But in this situation, the
# check that is being discussed wouldn't help at all.
# Read dialog's output on its stderr
try:
child_output = os.fdopen(child_rfd, "rb").read()
# Now, since the file object has no reference anymore, the
# standard IO stream behind it will be closed, causing the
# end of the the pipe we used to read dialog's output on its
# stderr to be closed (this is important, otherwise invoking
# dialog enough times will eventually exhaust the maximum number
# of open file descriptors).
except IOError, v:
raise PythonDialogIOError(v)
return (exit_code, child_output)
def _perform(self, cmdargs, **kwargs):
"""Perform a complete dialog-like program invocation.
This function invokes the dialog-like program, waits for its
termination and returns its exit status and whatever it wrote